#pragma semicolon 1 // Force strict semicolon mode.

#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <tfx>
#include <psycore>


#define PLUGIN_NAME              "tPsyPresents"
#define PLUGIN_AUTHOR            "Thrawn, psyduck"
#define PLUGIN_VERSION           "1.0.0.0"
#define PLUGIN_CONTACT           "http://aaa.einfachonline.net/"

#define SOUND_PRESENT 			 "misc/happy_birthday.wav"
#define SOUND_PRESENTEXPLODE	 "items/pumpkin_explode1.wav"

new Handle:g_hCvarEnable;
new Handle:g_hCvarShootable;
new Handle:g_hCvarHealth;
new Handle:g_hCvarDropChance;
new Handle:g_hCvarTTL;
new Handle:g_hCvarPickupByShooting;
new Handle:g_hCvarGoodChance;
new Handle:g_hCvarPushable;
new Handle:g_hCvarModel;
new Handle:g_hCvarSkin;

new g_iHealth;
new g_iModel;
new g_iSkin;
new Float:g_fDropChance;
new Float:g_fTTL;
new Float:g_fGoodChance;
new bool:g_bEnable;
new bool:g_bPickupByDestruction;
new bool:g_bPresentsShootable;
new bool:g_bPushable;

new g_iEffect[MAXPLAYERS+1] = {-1,...};

new String:g_sPresentModels[4][] = {
	"models/effects/bday_gib03.mdl",
	"models/effects/bday_gib02.mdl",
	"models/effects/bday_gib01.mdl",
	"models/effects/bday_gib04.mdl"
};


public Plugin:myinfo = {
    name        = PLUGIN_NAME,
    author      = PLUGIN_AUTHOR,
    description = PLUGIN_NAME,
    version     = PLUGIN_VERSION,
    url         = PLUGIN_CONTACT
};

public OnPluginStart() {
	CreateConVar("sm_tpp_version", PLUGIN_VERSION, "Version of tPsyPresents plugin.", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD);
	g_hCvarEnable = CreateConVar("sm_tpp_enable", "1", "Enable tPsyPresents presents", FCVAR_PLUGIN, true, 0.0, true, 1.0);		
	g_hCvarDropChance = CreateConVar("sm_tpp_chance", "0.2", "Chance a present spawns on death", FCVAR_PLUGIN, true, 0.0, true, 1.0);	
	g_hCvarTTL = CreateConVar("sm_tpp_ttl", "0", "A presents time to live. After that it will be destroyed. 0 = unlimited", FCVAR_PLUGIN, true, 0.0);	
	g_hCvarGoodChance = CreateConVar("sm_tpp_goodchance", "0.5", "RandomEffects: Decimal chance for a good effect versus a bad effect (between 0.0 and 1.0, default 0.5)", FCVAR_PLUGIN, true, 0.0, true, 1.0);
	
	g_hCvarShootable = CreateConVar("sm_tpp_shootable", "1", "Make presents destructable", FCVAR_PLUGIN, true, 0.0, true, 1.0);	
	g_hCvarHealth = CreateConVar("sm_tpp_health", "150", "If presents are shootable, how much health do they have", FCVAR_PLUGIN, true, 1.0);		
	g_hCvarPickupByShooting = CreateConVar("sm_tpp_shootable_pickup", "1", "You can pickup presents by destroying them", FCVAR_PLUGIN, true, 0.0, true, 1.0);	
	
	g_hCvarPushable = CreateConVar("sm_tpp_pushable", "1", "You can push presents away by walking against them (if you cant pick it up)", FCVAR_PLUGIN, true, 0.0, true, 1.0);	
	g_hCvarModel = CreateConVar("sm_tpp_usemodel", "0", "Use a specific model (1-4) or choose randomly (0)", FCVAR_PLUGIN, true, 0.0, true, 4.0);	
	g_hCvarSkin = CreateConVar("sm_tpp_useskin", "0", "Use a specific skin (2 red, 3 blue), choose by team (0) or choose randomly (1)", FCVAR_PLUGIN, true, 0.0, true, 4.0);	
	
	HookConVarChange(g_hCvarEnable, Cvar_Changed);
	HookConVarChange(g_hCvarShootable, Cvar_Changed);
	HookConVarChange(g_hCvarHealth, Cvar_Changed);
	HookConVarChange(g_hCvarDropChance, Cvar_Changed);
	HookConVarChange(g_hCvarTTL, Cvar_Changed);
	HookConVarChange(g_hCvarPickupByShooting, Cvar_Changed);
	HookConVarChange(g_hCvarGoodChance, CvarChanged);
	HookConVarChange(g_hCvarPushable, CvarChanged);	
	HookConVarChange(g_hCvarModel, CvarChanged);
	HookConVarChange(g_hCvarSkin, CvarChanged);
	
	HookEvent("player_death", Event_PlayerDeath);

	RegAdminCmd("sm_tpp_drop", Command_DropLoot, ADMFLAG_BAN);
}

public CvarChanged(Handle:convar, const String:oldValue[], const String:newValue[])
{
	OnConfigsExecuted();
}

public OnConfigsExecuted() {
	g_bEnable = GetConVarBool(g_hCvarEnable);
	g_bPresentsShootable = GetConVarBool(g_hCvarShootable);
	g_bPushable = GetConVarBool(g_hCvarPushable);
	g_iHealth = GetConVarInt(g_hCvarHealth);
	g_fDropChance = GetConVarFloat(g_hCvarDropChance);
	g_fTTL = GetConVarFloat(g_hCvarTTL);
	g_bPickupByDestruction = GetConVarBool(g_hCvarPickupByShooting);
	g_fGoodChance = GetConVarFloat(g_hCvarGoodChance);
	g_iModel = GetConVarInt(g_hCvarModel);
	g_iSkin = GetConVarInt(g_hCvarSkin);
}

public Cvar_Changed(Handle:convar, const String:oldValue[], const String:newValue[]) {
	OnConfigsExecuted();
}

public OnMapStart()
{
	for(new i = 0; i < sizeof(g_sPresentModels); i++) {
		PrecacheModel(g_sPresentModels[i], true);
	}

	PrecacheSound(SOUND_PRESENT, true);
	PrecacheSound(SOUND_PRESENTEXPLODE, true);
	
}

public psyRTD_DisableEffect(iClient,iRTDEffectId) {	
	ShowEffectWornOff(iClient, iRTDEffectId);
	g_iEffect[iClient] = -1;
}


stock bool:PresentPickup(client, effect = -1, creator = -1) {
	if(effect == -1) {		
		new type = psyRTDEffectType_Bad;
		if (g_fGoodChance > GetRandomFloat(0.001, 0.999))
		{
			type = psyRTDEffectType_Good;
		}			

		//type = psyRTDEffectType_Good;
		new effectId = psyRTD_DoRandomEffect(client, type);

		if(effectId != -1) {		
			g_iEffect[client] = effectId;

			ShowEffectText(client, effectId);			
			return true;
		} else {
			PrintToChat(client, "You cant pickup this present");
			return false;
		}
	} else {
		PrintToChat(client, "You have picked up a present with effect number %i!", effect);
		return false;
		//psyRTD_DoEffect(client, psyRTDEffectType_Random);
	}
}

public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
	if (g_bEnable) {
		new victim = GetClientOfUserId(GetEventInt(event, "userid"));
		new killer = GetClientOfUserId(GetEventInt(event, "attacker"));
		new deathFlags = GetEventInt(event, "death_flags");
		
		// Stop the victim's effect
		if(g_iEffect[victim] != -1)
			psyRTD_EffectIsNowDisabled(victim,g_iEffect[victim]);
		
		// If this wasn't a suicide or dead ringer
		if ((victim != killer) && (!(deathFlags & 32))) {
			
			// Drop a present based on chance for the killer
			if (GetRandomFloat() < g_fDropChance) {				
				new Float:pos[3];
				GetClientAbsOrigin(victim, pos);
			
				DropPresent(pos, killer, -1, victim);
			}
		}
	}
	
	return Plugin_Continue;
}


public Action:Command_DropLoot(client, args) {	
	if(g_bEnable) {		
		new Float:pos[3];
		GetClientAbsOrigin(client, pos);
		
		// *FIXME* Handle dropping of presents with a specific effect
		DropPresent(pos, -1, -1, client);
	}
}

stock DropPresent(Float:pos[3], owner=-1, effect=-1, creator=-1)
{			
	new present = CreateEntityByName("prop_physics");
	
	if (present) {
		//g_iModel :: Set entity model accordingly
		SetEntityModel(present, g_iModel == 0 ? g_sPresentModels[GetRandomInt(0,sizeof(g_sPresentModels)-1)] : g_sPresentModels[g_iModel]);
		
		//g_iSkin :: Use a specific skin (2 red, 3 blue), choose by team (0) or choose randomly (1)
		new skin = 0;	
		if(g_iSkin == 0 && creator != -1) {
			//by team
			new iTeam = GetClientTeam(creator);
			skin = iTeam - 2;
			if(skin < 0) {
				//creator was spectator or unassigned, choose random then
				skin = GetRandomInt(0, 1);
			}
		} else {
			if(g_iSkin == 2 || g_iSkin == 3) {
				//specific
				skin = g_iSkin - 2;			
			} else if(g_iSkin == 1 || creator == -1) {
				//random
				skin = GetRandomInt(0, 1);
			}
		
		}
		
		SetEntProp(present, Prop_Send, "m_nSkin", skin);
		
		if(g_bPresentsShootable) {
			SetEntProp(present, Prop_Data, "m_takedamage", 2); 
		}
		
		DispatchSpawn(present);		
		TeleportEntity(present, pos, NULL_VECTOR, NULL_VECTOR);
				
		SetEntProp(present, Prop_Send, "m_hOwnerEntity", owner);
		//SetEntProp(present, Prop_Send, "m_hOwnerEntity", -1);
		
		if(g_bPresentsShootable) {
			SetEntProp(present, Prop_Data, "m_takedamage", 2); 

			SetEntProp(present, Prop_Data, "m_nSolidType", 6 );
			SetEntProp(present, Prop_Send, "m_nSolidType", 6 );

			SetEntProp(present, Prop_Data, "m_iMaxHealth", g_iHealth);
			SetEntProp(present, Prop_Data, "m_iHealth", g_iHealth);

			AcceptEntityInput(present, "DisableCollision" );
			AcceptEntityInput(present, "EnableCollision" );

			SDKHook(present, SDKHook_OnTakeDamage, PresentTakeDamage);
		}
		
		if (owner != -1) {
			SetEntityRenderColor(present, 255, 255, 255, 128);
			SetEntityRenderMode(present, RENDER_TRANSTEXTURE);
			
			CreateTimer(3.0, Timer_PresentOwner, present);
		} else {
			SetEntProp(present, Prop_Data, "m_CollisionGroup", 2);
			
			CreateTimer(1.0, Timer_PresentCollision, present);
		}

		if(g_fTTL > 0.0) {
			// *FIXME* Handle timer if present gets picked up
			CreateTimer(g_fTTL, Timer_PresentKill, present);
		}
		
		SDKHook(present, SDKHook_StartTouch, OnPresentTouch);


		SetEntProp(present, Prop_Send, "m_hEffectEntity", effect);
		SetEntProp(present, Prop_Send, "m_hLightingOrigin", creator); 
	
		// All entities created should have a targetname of 'present' to make cleaning up easier
		DispatchKeyValue(present, "targetname", "present");
		
		// Play a sound to let everyone know a present spawned
		EmitAmbientSound(SOUND_PRESENT, pos);
		
		// Make it pretty
		CreateParticle("mini_fireworks", 5.0, present, ATTACH_NORMAL);
		CreateParticle("bday_confetti", 5.0, present, ATTACH_NORMAL);
	}
}

public PresentExplode(present) {
	CreateParticle("ExplosionCore_MidAir", 1.0, present);
	
	new Float:pos[3];
	GetEntPropVector(present, Prop_Send, "m_vecOrigin", pos);		
	// *FIXME* Deal damage in the next frame
	//new creator = GetEntProp(present, Prop_Send, "m_hLightingOrigin"); 	
	//DealDamage(pos, 50, creator, DMG_BLAST);
	
	EmitAmbientSound(SOUND_PRESENTEXPLODE, pos);
}



public Action:PresentTakeDamage(present, &attacker, &inflictor, &Float:damage, &damagetype) {	
	new health = GetEntProp(present, Prop_Data, "m_iHealth");
		
	if(health - damage < 1) {
		PresentExplode(present);
		
		if(g_bPickupByDestruction) {
			if(attacker > 0 && attacker <= MaxClients && IsClientInGame(attacker) && g_iEffect[attacker] == -1) {
				new effect = GetEntProp(present, Prop_Send, "m_hEffectEntity");
				new creator = GetEntProp(present, Prop_Send, "m_hLightingOrigin"); 
				
				PresentPickup(attacker, effect, creator);
			}
		}
	}
}

public Action:Timer_PresentKill(Handle:timer, any:present)
{
	if (IsValidEntity(present)) {
		PresentExplode(present);
		AcceptEntityInput(present, "Kill");		
	}
}


public Action:Timer_PresentCollision(Handle:timer, any:present)
{
	if (IsValidEntity(present)) {
		SetEntProp(present, Prop_Data, "m_CollisionGroup", 0);
	}
}

public Action:Timer_PresentOwner(Handle:timer, any:present)
{
	if (IsValidEntity(present)) {
		SetEntityRenderColor(present, 255, 255, 255, 255);
		SetEntProp(present, Prop_Send, "m_hOwnerEntity", -1);
	}
}

public OnPresentTouch(present, activator) {		
	if (g_bEnable) {
		if ((activator > 0) && (activator <= MaxClients) && IsClientInGame(activator) && !IsClientInKickQueue(activator)) {	
			new owner = GetEntProp(present, Prop_Send, "m_hOwnerEntity");
			
			if(g_iEffect[activator] == -1 && ((owner == activator) || (owner == -1))) {	
				new effect = GetEntProp(present, Prop_Send, "m_hEffectEntity");
				new creator = GetEntProp(present, Prop_Send, "m_hLightingOrigin"); 
				
				AcceptEntityInput(present, "Kill");
				PresentPickup(activator, effect, creator);				
			} else if(g_bPushable) {
				//Push present around
				new Float:posPresent[3];
				GetEntPropVector(present, Prop_Send, "m_vecOrigin", posPresent);		

				new Float:posClient[3];
				GetClientAbsOrigin(activator, posClient);

				new Float:vec[3];
				MakeVectorFromPoints(posClient, posPresent, vec);
				NormalizeVector(vec, vec);
				ScaleVector(vec, 600.0);
				TeleportEntity(present, NULL_VECTOR, NULL_VECTOR, vec);			
			}
		}
	}
}

public ShowEffectText(client, effectId)
{
	new String:title[64];
	psyRTD_GetEffectName(effectId, title);

	new String:desc[64];
	psyRTD_GetEffectDescription(effectId, desc);

	SetHudTextParams(-1.0, 0.4, 5.0, 255, 255, 255, 255);		
	ShowHudText(client, 3, "%s\n\n\n\n\n%s", title, desc);		
}

public ShowEffectWornOff(client, effectId)
{
	new String:title[64];
	psyRTD_GetEffectName(effectId, title);

	SetHudTextParams(-1.0, 0.4, 5.0, 255, 255, 255, 200);
	ShowHudText(client, 3, "%s has worn off", title);
}
